package com.xx.sg.tcp.server;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xx.sg.own.entity.CommunicationTerminal;
import com.xx.sg.own.entity.CommunicationTerminalLog;
import com.xx.sg.own.service.CommunicationTerminalLogService;
import com.xx.sg.own.service.CommunicationTerminalService;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.springframework.stereotype.Component;

import java.math.BigInteger;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ChannelHandler.Sharable
public class TcpServerHandler extends ChannelInboundHandlerAdapter {

    private final CommunicationTerminalService communicationTerminalService;
    private final CommunicationTerminalLogService communicationTerminalLogService;

    // 通过构造方法注入
    public TcpServerHandler(CommunicationTerminalService communicationTerminalService,
                            CommunicationTerminalLogService communicationTerminalLogService) {
        this.communicationTerminalService = communicationTerminalService;
        this.communicationTerminalLogService = communicationTerminalLogService;
    }

    /**
     * 管理一个全局map，保存连接进服务端的通道数量
     */
    private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();

    /**
     * @param ctx
     * @DESCRIPTION: 有客户端连接服务器会触发此函数
     * @return: void
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {

        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();
        int clientPort = insocket.getPort();

        //获取连接通道唯一标识
        ChannelId channelId = ctx.channel().id();


        //如果map中不包含此连接，就保存连接
        if (CHANNEL_MAP.containsKey(channelId)) {
            System.out.println("ODF-Socket------客户端【" + channelId + "】是连接状态，连接通道数量: " + CHANNEL_MAP.size());

        } else {
            //保存连接
            CHANNEL_MAP.put(channelId, ctx);
            System.out.println("ODF-Socket------客户端【" + channelId + "】连接netty服务器[IP:" + clientIp + "--->PORT:" + clientPort + "]");
            System.out.println("ODF-Socket------连接通道数量: " + CHANNEL_MAP.size());
        }
    }

    /**
     * @DESCRIPTION: 有客户端终止连接服务器会触发此函数
     * @return: void
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {

        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();

        String clientIp = insocket.getAddress().getHostAddress();

        ChannelId channelId = ctx.channel().id();

        //包含此客户端才去删除
        if (CHANNEL_MAP.containsKey(channelId)) {
            //删除连接
            CHANNEL_MAP.remove(channelId);
            System.out.println();
            System.out.println("ODF-Socket------客户端【" + channelId + "】退出netty服务器[IP:" + clientIp + "--->PORT:" + insocket.getPort() + "]");
            System.out.println("ODF-Socket------连接通道数量: " + CHANNEL_MAP.size());
        }
    }

    /**
     * @DESCRIPTION: 有客户端发消息会触发此函数
     * @return: void
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        if (msg == null) {
            System.err.println("ODF-Socket------加载客户端报文为空,请联系厂商!");
        }
        System.out.println("ODF-Socket------加载客户端报文......【" + ctx.channel().id() + "】" + " :" + msg);

        /**
         *  下面可以解析数据，保存数据，生成返回报文，将需要返回报文写入write函数
         *   在这里可以设置异步执行 提交任务到该channel的taskQueue 中
         */

        ctx.channel().eventLoop().execute(() -> {
            ByteBuf buf = (ByteBuf) msg;
            byte[] bytes = new byte[buf.readableBytes()];
            buf.readBytes(bytes);  // 读取 ByteBuf 内容
            buf.release();
            parseData(bytes);
        });
    }

    /**
     * @param msg       需要发送的消息内容
     * @param channelId 连接通道唯一id
     * @DESCRIPTION: 服务端给客户端发送消息
     * @return: void
     */
    public void channelWrite(ChannelId channelId, Object msg) throws Exception {

        ChannelHandlerContext ctx = CHANNEL_MAP.get(channelId);

        if (ctx == null) {
            System.out.println("ODF-Socket------通道【" + channelId + "】不存在");
            return;
        }
        if (msg == null || msg == "") {
            System.out.println("ODF-Socket------服务端响应空的消息");
            return;
        }

        //将客户端的信息直接返回写入ctx
        ctx.write(msg);
        //刷新缓存区
        ctx.flush();
    }


    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

        String socketString = ctx.channel().remoteAddress().toString();

        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                System.out.println("ODF-Socket------Client: " + socketString + " READER_IDLE 读超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                System.out.println("ODF-Socket------Client: " + socketString + " WRITER_IDLE 写超时");
                ctx.disconnect();
            } else if (event.state() == IdleState.ALL_IDLE) {
                System.out.println("ODF-Socket------Client: " + socketString + " ALL_IDLE 总超时");
                ctx.disconnect();
            }
        }
    }

    /**
     * @param ctx
     * @DESCRIPTION: 发生异常会触发此函数
     * @return: void
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        System.out.println();
        ctx.close();
        System.out.println("ODF-Socket------" + ctx.channel().id() + " 发生了错误,此连接被关闭" + "此时连通数量: " + CHANNEL_MAP.size());
        //cause.printStackTrace();
    }


    // 将 byte[] 转换为十六进制字符串
    private String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b)); // 每个字节转换成 2 位 HEX
        }
        return sb.toString().trim();
    }

    private String[] bytesToHexArray(byte[] bytes) {
        String[] hexArray = new String[bytes.length];
        for (int i = 0; i < bytes.length; i++) {
            hexArray[i] = String.format("%02X", bytes[i]); // 转换为 2 位 HEX 字符串
        }
        return hexArray;
    }

    //[-85, -86, 57, 0, 33, 16, -80, 3, 74, 121, -51, 2, 36, 71, 80, 71, 76, 76, 44, 52, 57, 49, 55, 46, 48, 56, 57, 56, 44, 78, 44, 49, 50, 48, 52, 51, 46, 51, 50, 48, 48, 44, 69, 44, 49, 53, 50, 56, 49, 52, 44, 65, 42, 55, 52, 13, 10]
    private void parseData(byte[] bytes) {
        String[] dataArray = bytesToHexArray(bytes);
        String[] ssiArray = Arrays.copyOfRange(dataArray, 8, 12);

        byte[] nmeaArray = Arrays.copyOfRange(bytes, 12, bytes.length - 1);
        String nmeaStr = new String(nmeaArray, StandardCharsets.US_ASCII);
        String devId = getSSI(ssiArray);

        CommunicationTerminalLog communicationTerminalLog = parseGPGLL(nmeaStr);
        communicationTerminalLog.setDevId(devId);
        communicationTerminalLog.setDataText(Arrays.toString(bytes));

        //查询是否有记录
        LambdaQueryWrapper<CommunicationTerminal> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(CommunicationTerminal::getDevId, devId);
        CommunicationTerminal communicationTerminal = communicationTerminalService.getOne(wrapper);
        LocalDateTime now = LocalDateTime.now();

        if (communicationTerminal != null) {
            communicationTerminal.setLatitude(communicationTerminalLog.getLatitude());
            communicationTerminal.setLongitude(communicationTerminalLog.getLongitude());
            communicationTerminal.setTime(communicationTerminalLog.getTime());
            communicationTerminal.setDataStatus(communicationTerminalLog.getDataStatus());
            communicationTerminal.setUpdateTime(now);
            communicationTerminal.setDataText(communicationTerminalLog.getDataText());
            communicationTerminalService.updateById(communicationTerminal);
        } else {
            communicationTerminal = new CommunicationTerminal();
            communicationTerminal.setLatitude(communicationTerminalLog.getLatitude());
            communicationTerminal.setLongitude(communicationTerminalLog.getLongitude());
            communicationTerminal.setTime(communicationTerminalLog.getTime());
            communicationTerminal.setDataStatus(communicationTerminalLog.getDataStatus());
            communicationTerminal.setUpdateTime(now);
            communicationTerminal.setDataText(communicationTerminalLog.getDataText());
            communicationTerminal.setDevId(devId);
            communicationTerminalService.save(communicationTerminal);
        }

        communicationTerminalLog.setCommunicationTerminalId(communicationTerminal.getId());
        communicationTerminalLogService.save(communicationTerminalLog);
    }


    private String getSSI(String[] ssiArray) {
        StringBuilder hexStr = new StringBuilder();
        for (int i = ssiArray.length; i > 0; i--) {
            hexStr.append(ssiArray[i - 1]);
        }
        return new BigInteger(hexStr.toString(), 16).toString(10);
    }


    public CommunicationTerminalLog parseGPGLL(String gpgll) {
        // 去掉开头的 "$" 和结尾的校验和部分

        System.err.println(gpgll.substring(gpgll.indexOf("$") + 1, gpgll.indexOf("*") + 3));
        String[] parts = gpgll.substring(gpgll.indexOf("$") + 1, gpgll.indexOf("*") + 3).split(",");

        // 纬度
        String latitude = parts[1];
        String latitudeDirection = parts[2];
        double lat = parseLatitude(latitude);
        if ("S".equals(latitudeDirection)) {
            lat = -lat;
        }

        // 经度
        String longitude = parts[3];
        String longitudeDirection = parts[4];
        double lon = parseLongitude(longitude);
        if ("W".equals(longitudeDirection)) {
            lon = -lon;
        }

        // 时间
        String time = parts[5];
        String formattedTime = formatTime(time);

        // 有效性
        String validity = parts[6];

        // 打印解析结果
//        System.out.println("Latitude: " + lat);
//        System.out.println("Longitude: " + lon);
//        System.out.println("Time (UTC): " + formattedTime);
//        System.out.println("Validity: " + validity.split("\\*")[0]);

        return CommunicationTerminalLog.builder()
                .latitude(String.format("%.6f", lat))
                .longitude(String.format("%.6f", lon))
                .time(formattedTime)
                .dataStatus(validity.split("\\*")[0])
                .build();
    }

    private double parseLatitude(String latitude) {
        double degree = Double.parseDouble(latitude.substring(0, 2));
        double minutes = Double.parseDouble(latitude.substring(2));
        return degree + (minutes / 60);
    }

    private double parseLongitude(String longitude) {
        double degree = Double.parseDouble(longitude.substring(0, 3));
        double minutes = Double.parseDouble(longitude.substring(3));
        return degree + (minutes / 60);
    }

    private String formatTime(String time) {
        String hour = time.substring(0, 2);
        String minute = time.substring(2, 4);
        String second = time.substring(4, 6);
        return hour + ":" + minute + ":" + second;
    }
}
